iT邦幫忙

2024 iThome 鐵人賽

DAY 26
1
JavaScript

PM說: RD大大,這個功能要怎麼寫啊?系列 第 26

前端工程師說: 可以推薦用 js 寫爬蟲的套件嗎?

  • 分享至 

  • xImage
  •  

hero

前言

爬蟲是什麼?
在筆者大一時還分不清楚一堆名詞的意思,第一次聽到還以為是在講爬蟲類(離題了~)
/images/emoticon/emoticon01.gif

爬蟲

爬蟲 = 網路爬蟲(web crawler) = 用程式call API = 拿一坨文字做資料處理
上面的API是廣義的(透過網址拿資料的意思)

舉例:
今天我去逛台中市消防局
https://www.fire.taichung.gov.tw/caselist/index.asp?Parser=99,8,226

人類看到的:
fire-web

瀏覽器看到的:
fire-source
其實就是html文字

我寫了程式碼去撈文字並處理成我要的格式

{
  result: true,
  message: 'ok',
  data: [
    {
      '受理時間': '2024/09/23 20:25:30',
      '案類': '其他',
      '案別': '其他',
      '發生地點': '台中市豐原區直興街',
      '派遣分隊': '豐南分隊',
      '執行狀況': '已出動'
    }
    //...etc
  ]
}

demo

讓電腦爬文字,圖片蒐集資料再依照需求做各種處理~
ex: 發lineNotify訊息, 寄信通知, 存到Notion筆記 ...etc

到這邊相信讀者對爬蟲有概念了~

接著說明一下怎麼在本地開發流程:

  1. 安裝 Node.js 18 以上
  2. 開個資料夾,產生一個index.js檔案
  3. npm init 一路Enter到底
  4. npm cheerio
  5. 撰寫程式碼~
  6. node .\index.js

cheerio

推薦的套件:
https://github.com/cheeriojs/cheerio

cheerio 讓nodejs能愉快地使出瀏覽器在用的Selector(選擇器語法)


//!! Node.js 18 LTS or later !!
//import * as cheerio from "cheerio";
const cheerio = require("cheerio");
//body就是拿到的那陀html文字
 const $ = cheerio.load(body);

重要語法

這邊整理出很常用到的語法,可優先學習~

  • .find(selector)
  • .text()
  • .attr('class')
  • .each()
  • .get( [i] )

語法文件(列的很詳細)
https://github.com/cheeriojs/cheerio/wiki/Chinese-README

趕緊寫出你的第一套爬蟲程式吧~~


本日程式碼

//!! Node.js 18 LTS or later !!
// npm i cheerio
const cheerio = require("cheerio");

main();

async function main() {
  const url =
    "https://www.fire.taichung.gov.tw/caselist/index.asp?Parser=99,8,226";

  const response = await fetchUrl(url, handleTaichungFireCrawler);
  console.log(response);
}

function handleTaichungFireCrawler(body) {
  const $ = cheerio.load(body);

  const dataList = [];
  const listItems = $(".rwd-table > li:not(.list_head)");

  const getAddress = (dom) => {
    const childBtn = $(dom).find("button");
    return childBtn.attr("js_addr").trim();
  };
  const getDomText = (dom) => $(dom).text().trim();

  listItems.each((index, element) => {
    const fields = $(element).find("span");

    const data = {
      受理時間: getDomText(fields.get(0)),
      案類: getDomText(fields.get(1)),
      案別: getDomText(fields.get(2)),
      發生地點: getAddress(fields.get(3)),
      派遣分隊: getDomText(fields.get(5)),
      執行狀況: getDomText(fields.get(6)),
    };
    dataList.push(data);
  });

  return {
    result: dataList.length > 0,
    message: dataList.length > 0 ? "ok" : "無資料",
    data: dataList,
  };
}

async function fetchUrl(url, func) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const body = await response.text();
    return func(body);
  } catch (error) {
    console.error("Fetch error:", error);
    return { result: false, message: error.message };
  }
}

上一篇
PM 說: 可以用網頁實現機上盒的 App 操作 UI 嗎?
下一篇
PM 說: Line Notify 免費的限制是什麼?
系列文
PM說: RD大大,這個功能要怎麼寫啊?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言